package com.danicsoft.daide.command;

import java.util.List;

import com.danicsoft.daide.token.TokenGroup;
import com.danicsoft.daide.token.TokenMap.Commands;

/**
 * Implements the MIS command. From Eric Wald's <a
 * href="http://pydip.brainshell.org/docs/syntax.html">protocol specification</a>,
 * v0.14:
 * <h4><a name="MIS">server_message = MIS</a> (unit) (unit) ...</h4>
 * <h4>server_message = MIS (unit MRT (province_list)) (unit MRT
 * (province_list)) ...</h4>
 * <h4>server_message = MIS (number)</h4>
 * <p>
 * This command can be sent by the server immediately after the last <b>THX</b>
 * message following a <b>SUB</b> command. If sent, it indicates that the
 * server does not have a full set of orders. The first form is used during a
 * movement phase, and indicates the list of units which have not been ordered.
 * The second form is used during a retreat phase, and indicates the list of
 * dislodged units which have not been ordered. The third form is used during a
 * build phase. If <b>number</b> is positive, it indicates that the player must
 * order that many more disbands. If <b>number</b> is negative, it indicates
 * that the player must order that many more builds.
 * </p>
 * <p>
 * The first two forms of the <b>MIS</b> command use exactly the same parameter
 * format as <a href="#NOW">the <b>NOW</b> command</a>.
 * </p>
 * <p>
 * The player may request a copy of the current <b>MIS</b> command by sending
 * <b>MIS</b> with no parameters. The server will respond with <b>REJ (MIS)</b>
 * if the game has not yet started or has ended. If there are no outstanding
 * orders, then the server will reply with <b>MIS</b> with no parameters.
 * </p>
 */
public class MissingCommand extends Command {

	/**
	 * A list of positions we are missing orders for.
	 */
	private List<UnitPos> missingPositions;

	/**
	 * The number of removals we have left.
	 */
	private int removalsLeft;

	/**
	 * Whether or not this MissingCommand contains position information or
	 * removal information.
	 */
	private boolean isPositions;

	/**
	 * Construct a new MissingCommand from the tokens specified.
	 * 
	 * @param tokens
	 *        The tokens to construct this <code>MissingCommand</code> from.
	 *        They must either be a single integer, or a list of unit positions.
	 * @throws ParseException
	 *         If there was a problem parsing the tokens.
	 */

	public MissingCommand(TokenGroup tokens) throws ParseException {
		super(tokens);
	}

	/**
	 * Constructs a new MissingCommand from the list of missing positions.
	 * 
	 * @param missingPositions
	 *        The list of commands that are missing.
	 */
	public MissingCommand(List<UnitPos> missingPositions) {
		isPositions = true;
		this.missingPositions = missingPositions;
	}

	/**
	 * Constructs a new Missing Command that informs how many removals are
	 * missing.
	 * 
	 * @param removals
	 *        The number of removals missing.
	 */
	public MissingCommand(int removals) {
		isPositions = false;
		removalsLeft = removals;
	}

	@Override
	public byte getType() {
		return Commands.MIS;
	}

	@Override
	public String toString() {
		if (isPositions) {
			StringBuilder sb = new StringBuilder("Missing positions for: ");
			for (UnitPos pos : missingPositions) {
				sb.append(pos.toString());
				sb.append(", ");
			}
			return sb.toString();
		}
		else {
			if (removalsLeft < 0) {
				return "Missing " + -removalsLeft + " build orders";

			}
			else {
				return "Missing " + removalsLeft + " remove orders";
			}
		}
	}

	public void doIO(TokenIO tokenIO, TokenGroup tokens) throws ParseException {

		isPositions = tokenIO.doIfGroup(tokens, isPositions);
		if (isPositions) {

			missingPositions = tokenIO.doList(tokens, missingPositions, new RetreatableUnitPos());
		}
		else {
			removalsLeft = tokenIO.doInteger(tokens, removalsLeft);
		}

	}

	/**
	 * Gets whether we are missing positions or removal
	 * 
	 * @return True if we are missing movement or retreat orders, false if we
	 *         are missing build or removal orders.
	 */
	public boolean isPositions() {
		return isPositions;
	}

	/**
	 * Gets the positions that are missing. This method is only valid if
	 * isPositions is true.
	 * 
	 * @return The missing positions
	 */
	public List<UnitPos> getMissingPositions() {
		return missingPositions;
	}

	/**
	 * Get the number of removals left. If negative, the number of builds left.
	 * 
	 * @return The removals left
	 */
	public int getRemovalsLeft() {
		return removalsLeft;
	}
}
